/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.hiof.moraa.server.service;

import com.hiof.moraa.server.service.model.GeoPoint;
import com.hiof.moraa.server.service.model.MapPoint;
import com.hiof.moraa.server.service.model.RelativePosition;
import java.util.List;

/**
 *
 * @author Bergy
 */
public class RouteGeographyService {

    private GeometryService geometryService;

    public void setGeometryService(GeometryService geometryService) {
        this.geometryService = geometryService;
    }

    /**
     * Find the distance between two ponts in one route
     *
     * @param p1: point1
     * @param p2: point2
     * @param mapPoints: routeMap points (which can get from MapService)
     * @return: distance (meters)
     * @throws Exception
     */
    public double distanceBetween2PointsInOneRoute(GeoPoint p1, GeoPoint p2, List<MapPoint> mapPoints) throws Exception {
        int indexOfClosestPoint1 = findIndexOfClosestPointOnTheRoute(p1, mapPoints);
        double distanceFromPoint1ToTheRoute = geometryService.distanceByMeters(p1, mapPoints.get(indexOfClosestPoint1).getGeoPoint());
        int indexOfClosetPoint2 = findIndexOfClosestPointOnTheRoute(p2, mapPoints);
        double distanceFromPoint2ToTheRoute = geometryService.distanceByMeters(p2, mapPoints.get(indexOfClosetPoint2).getGeoPoint());
        double distanceBetween2MapPointsByIndex = distanceBetween2MapPoints(indexOfClosestPoint1, indexOfClosetPoint2, mapPoints);
        return distanceBetween2MapPointsByIndex + distanceFromPoint1ToTheRoute + distanceFromPoint2ToTheRoute;
    }

    /**
     * Find the relative position between 2 points
     *
     * @param p1: point 1
     * @param p2: point 2
     * @param mapPoints: routeMap points (which can get from MapService)
     * @return: Forward if point 2 > point 1 Behind if point 2 < point 1
     */
    public RelativePosition findRelativePositionInOneRoute(GeoPoint p1, GeoPoint p2, List<MapPoint> mapPoints) {
        int indexOfClosestPoint1 = findIndexOfClosestPointOnTheRoute(p1, mapPoints);
        System.out.println("Index of athlete: " + indexOfClosestPoint1);
        int indexOfClosestPoint2 = findIndexOfClosestPointOnTheRoute(p2, mapPoints);
        System.out.println("Index of target: " + indexOfClosestPoint2);
        if (indexOfClosestPoint1 < indexOfClosestPoint2) {
            return RelativePosition.FORWARD;
        } else if (indexOfClosestPoint1 > indexOfClosestPoint2) {
            return RelativePosition.BEHIND;
        } else {
            GeoPoint startPoint = mapPoints.get(0).getGeoPoint();
            double distanceOfPoint1 = geometryService.distanceByMeters(startPoint, p1);
            double distanceOfPoint2 = geometryService.distanceByMeters(startPoint, p2);
            if (distanceOfPoint1 < distanceOfPoint2) {
                return RelativePosition.FORWARD;
            } else {
                return RelativePosition.BEHIND;
            }
        }
    }

    /**
     * Check whether athlete pass target or not with the mechanism athlete will
     * pass the target if: He actually pass the target or He around the target
     * in the area with 100 meter radius
     *
     * @param athleteGeoPoint
     * @param targetGeoPoint
     * @param mapPoints
     * @return
     */
    public boolean checkPassTargetByGeometryAlgorithms(GeoPoint athleteGeoPoint, GeoPoint targetGeoPoint, List<MapPoint> mapPoints) {
        double distance = geometryService.distanceByMeters(athleteGeoPoint, targetGeoPoint);
        if(distance < 100)
            return true;
        else{
            RelativePosition relativePosition = findRelativePositionInOneRoute(athleteGeoPoint, targetGeoPoint, mapPoints);
            if(relativePosition == RelativePosition.FORWARD){
                return false;
            }else{
                return true;
            }
        }
    }

    /**
     * Find the closest point on the route of the geopoint
     *
     * @param p1: GeoPoint
     * @param mapPoints
     * @return: Index of the point on the route
     */
    public int findIndexOfClosestPointOnTheRoute(GeoPoint p1, List<MapPoint> mapPoints) {
        int index = 0;
        double minDistance = Integer.MAX_VALUE;
        int i = 0;
        for (MapPoint mapPoint : mapPoints) {
            double distance = geometryService.distanceByMeters(p1, mapPoint.getGeoPoint());
            if (distance <= minDistance) {
                minDistance = distance;
                index = i;
            }
            i++;
        }
        return index;
    }

    private double distanceBetween2MapPoints(int indexOfClosestPoint1, int indexOfClosestPoint2, List<MapPoint> mapPoints) {
        double distanceFromStartOfPoint1 = mapPoints.get(indexOfClosestPoint1).getDistance();
        double distanceFromStartOfPoint2 = mapPoints.get(indexOfClosestPoint2).getDistance();

        return Math.abs(distanceFromStartOfPoint1 - distanceFromStartOfPoint2);
    }
}
